package com.nightonke.boommenu.BoomButtons;

import ohos.agp.animation.AnimatorProperty;
import ohos.agp.colors.RgbColor;

import ohos.app.Context;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.components.Image;
import ohos.agp.components.StackLayout;
import ohos.agp.components.Text.TruncationMode;
import ohos.agp.components.Text;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.multimodalinput.event.TouchEvent;

import com.nightonke.boommenu.BMBShadow;
import com.nightonke.boommenu.ButtonEnum;
import com.nightonke.boommenu.ResourceTable;
import com.nightonke.boommenu.Util;

import java.util.ArrayList;

/**
 * Created by Weiping Huang at 01:06 on 16/11/18
 * For Personal Open Source
 * Contact me at 2584541288@qq.com or nightonke@outlook.com
 */

public abstract class BoomButton extends StackLayout {

    // Basic
    protected Context context;
    protected int index = -1;
    protected InnerOnBoomButtonClickListener listener;
    protected OnBMClickListener onBMClickListener;
    protected boolean lastStateIsNormal = true;
    protected boolean ableToHighlight = true;
    protected StackLayout button;
    protected int buttonRadius;  // for simple/text-inside/text-outside circle button
    protected int buttonWidth;  // for ham button
    protected int buttonHeight;  // for ham button
    protected int buttonCornerRadius;  // for ham button
    protected boolean isRound;
    protected boolean rotateImage;
    protected boolean rotateText;
    protected boolean containsSubText;  // for ham button
    protected ButtonEnum buttonEnum = ButtonEnum.Unknown;
    private boolean touchable = false;

    // piece
    protected Color pieceColor = null;
    protected int pieceColorRes = 0;

    // Shadow
    protected boolean shadowEffect = true;
    protected int shadowOffsetX = 0;
    protected int shadowOffsetY = 0;
    protected int shadowRadius = 0;
    protected int shadowCornerRadius = 0;
    protected Color shadowColor;
    protected BMBShadow shadow;

    // Images
    protected int normalImageRes = 0;
    protected Element normalImageDrawable;
    protected int highlightedImageRes = 0;
    protected Element highlightedImageDrawable;
    protected int unableImageRes = 0;
    protected Element unableImageDrawable;
    protected Rect imageRect = null;
    protected Rect imagePadding = null;

    // Text
    protected int normalTextRes = 0;
    protected String normalText;
    protected int highlightedTextRes = 0;
    protected String highlightedText;
    protected int unableTextRes = 0;
    protected String unableText;
    protected Color normalTextColor;
    protected int normalTextColorRes = 0;
    protected Color highlightedTextColor;
    protected int highlightedTextColorRes = 0;
    protected Color unableTextColor;
    protected int unableTextColorRes = 0;
    protected Rect textRect = null;
    protected Rect textPadding = null;
    protected Font typeface;
    protected int maxLines;
    protected int textGravity;
    protected TruncationMode ellipsize;
    protected int textSize;

    // Sub text
    protected int subNormalTextRes = 0;
    protected String subNormalText;
    protected int subHighlightedTextRes = 0;
    protected String subHighlightedText;
    protected int subUnableTextRes = 0;
    protected String subUnableText;
    protected Color subNormalTextColor;
    protected int subNormalTextColorRes = 0;
    protected Color subHighlightedTextColor;
    protected int subHighlightedTextColorRes = 0;
    protected Color subUnableTextColor;
    protected int subUnableTextColorRes = 0;
    protected Rect subTextRect = null;
    protected Rect subTextPadding = null;
    protected Font subTypeface;
    protected int subMaxLines;
    protected int subTextGravity;
    protected TruncationMode subEllipsize;
    protected int subTextSize;

    // Text for text-outside-circle-button
    protected int textTopMargin;
    protected int textWidth;
    protected int textHeight;
    protected int trueRadius;

    // Button Colors
    protected boolean rippleEffect = false;
    protected Color normalColor;
    protected int normalColorRes = 0;
    protected Color highlightedColor;
    protected int highlightedColorRes = 0;
    protected Color unableColor;
    protected int unableColorRes = 0;
    protected boolean unable = false;
    protected boolean rippleEffectWorks = true;
    //protected RippleDrawable rippleDrawable;
    protected ShapeElement rippleDrawable;
    protected StateElement nonRippleBitmapDrawable;
    protected ShapeElement nonRippleGradientDrawable;

    // Views
    protected ComponentContainer layout;
    protected Image image;
    protected Text text;
    protected Text subText;

    protected BoomButton(Context contexts) {
        super(contexts);
    }
    protected AnimatorProperty ap;

    /**
     * initAttrs
     * @param builder
     */
    protected void initAttrs(BoomButtonBuilder builder) {
        index = builder.index;
        listener = builder.listener;
        onBMClickListener = builder.onBMClickListener;
        rotateImage = builder.rotateImage;
        rotateText = builder.rotateText;
        containsSubText = builder.containsSubText;

        pieceColor = builder.pieceColor;
        pieceColorRes = builder.pieceColorRes;

        shadowEffect = builder.shadowEffect;
        if (shadowEffect) {
            shadowOffsetX = builder.shadowOffsetX;
            shadowOffsetY = builder.shadowOffsetY;
            shadowRadius = builder.shadowRadius;
            shadowCornerRadius = builder.shadowCornerRadius;
            shadowColor = builder.shadowColor;
        }

        normalImageRes = builder.normalImageRes;
        highlightedImageRes = builder.highlightedImageRes;
        unableImageRes = builder.unableImageRes;
        normalImageDrawable = builder.normalImageDrawable;
        highlightedImageDrawable = builder.highlightedImageDrawable;
        unableImageDrawable = builder.unableImageDrawable;
        imageRect = builder.imageRect;
        imagePadding = builder.imagePadding;

        normalText = builder.normalText;
        normalTextRes = builder.normalTextRes;
        highlightedText = builder.highlightedText;
        highlightedTextRes = builder.highlightedTextRes;
        unableText = builder.unableText;
        unableTextRes = builder.unableTextRes;
        normalTextColor = builder.normalTextColor;
        normalTextColorRes = builder.normalTextColorRes;
        highlightedTextColor = builder.highlightedTextColor;
        highlightedTextColorRes = builder.highlightedTextColorRes;
        unableTextColor = builder.unableTextColor;
        unableTextColorRes = builder.unableTextColorRes;
        textRect = builder.textRect;
        textPadding = builder.textPadding;
        typeface = builder.typeface;
        maxLines = builder.maxLines;
        textGravity = builder.textGravity;
        ellipsize = builder.ellipsize;
        textSize = builder.textSize;

        subNormalText = builder.subNormalText;
        subNormalTextRes = builder.subNormalTextRes;
        subHighlightedText = builder.subHighlightedText;
        subHighlightedTextRes = builder.subHighlightedTextRes;
        subUnableText = builder.subUnableText;
        subUnableTextRes = builder.subUnableTextRes;
        subNormalTextColor = builder.subNormalTextColor;
        subNormalTextColorRes = builder.subNormalTextColorRes;
        subHighlightedTextColor = builder.subHighlightedTextColor;
        subHighlightedTextColorRes = builder.subHighlightedTextColorRes;
        subUnableTextColor = builder.subUnableTextColor;
        subUnableTextColorRes = builder.subUnableTextColorRes;
        subTextRect = builder.subTextRect;
        subTextPadding = builder.subTextPadding;
        subTypeface = builder.subTypeface;
        subMaxLines = builder.subMaxLines;
        subTextGravity = builder.subTextGravity;
        subEllipsize = builder.subEllipsize;
        subTextSize = builder.subTextSize;

        rippleEffect = builder.rippleEffect;
        normalColor = builder.normalColor;
        normalColorRes = builder.normalColorRes;
        highlightedColor = builder.highlightedColor;
        highlightedColorRes = builder.highlightedColorRes;
        unableColor = builder.unableColor;
        unableColorRes = builder.unableColorRes;
        unable = builder.unable;
        buttonRadius = builder.buttonRadius;
        buttonWidth = builder.buttonWidth;
        buttonHeight = builder.buttonHeight;
        isRound = builder.isRound;
        if (buttonEnum == ButtonEnum.SimpleCircle
                || buttonEnum == ButtonEnum.TextInsideCircle
                || buttonEnum == ButtonEnum.TextOutsideCircle) {
            if (isRound) buttonCornerRadius = builder.buttonRadius;
            else buttonCornerRadius = builder.buttonCornerRadius;
        } else buttonCornerRadius = builder.buttonCornerRadius;
        buttonCornerRadius = builder.buttonCornerRadius;
        rippleEffectWorks = rippleEffect;

        // for text-outside-circle-button
        textTopMargin = builder.textTopMargin;
        textWidth = builder.textWidth;
        textHeight = builder.textHeight;
        if (builder instanceof TextOutsideCircleButton.Builder) {
            int buttonAndShadowWidth = buttonRadius * 2 + shadowOffsetX * 2 + shadowRadius * 2;
            if (textWidth > buttonAndShadowWidth) {
                textRect = new Rect(
                        0,
                        shadowOffsetY + shadowRadius + buttonRadius * 2 + textTopMargin,
                        textWidth,
                        shadowOffsetY + shadowRadius + buttonRadius * 2 + textTopMargin + textHeight);
            } else {
                textRect = new Rect(
                        (buttonAndShadowWidth - textWidth) / 2,
                        shadowOffsetY + shadowRadius + buttonRadius * 2 + textTopMargin,
                        (buttonAndShadowWidth - textWidth) / 2 + textWidth,
                        shadowOffsetY + shadowRadius + buttonRadius * 2 + textTopMargin + textHeight);
            }
            trueRadius = (int) (Util.distance(
                    new Point(
                            shadowOffsetX + shadowRadius + buttonRadius,
                            shadowOffsetY + shadowRadius + buttonRadius),
                    new Point(
                            textRect.right,
                            textRect.bottom)) + 1);
            if (textWidth > buttonAndShadowWidth)
            {
                int dx = trueRadius - textWidth / 2;
                int dy = trueRadius - (shadowOffsetY + shadowRadius + buttonRadius);
                textRect.set(textRect.left+dx, textRect.top+dy, textRect.right+dx, textRect.bottom+dy);
            }
            else
            {
                int dx = trueRadius - (shadowOffsetX + shadowRadius + buttonRadius);
                int dy = trueRadius - (shadowOffsetY + shadowRadius + buttonRadius);
                textRect.set(textRect.left+dx, textRect.top+dy, textRect.right+dx, textRect.bottom+dy);

            }
        }

    }

    /**
     * init TextOutsideCircleButton Layout
     */
    protected void initTextOutsideCircleButtonLayout() {
        layout = (ComponentContainer) findComponentById(ResourceTable.Id_layout);
        StackLayout.LayoutConfig params = new LayoutConfig(trueRadius * 2, trueRadius * 2);
        layout.setLayoutConfig(params);
    }

    /**
     * init Shadow
     * @param shadowCornerRadiuss
     */
    protected void initShadow(int shadowCornerRadiuss) {
        if (shadowEffect) {
            shadow = (BMBShadow) findComponentById(ResourceTable.Id_shadow);
            shadow.setShadowOffsetX(shadowOffsetX);
            shadow.setShadowOffsetY(shadowOffsetY);
            shadow.setShadowColor(shadowColor);
            shadow.setShadowRadius(shadowRadius);
            shadow.setShadowCornerRadius(shadowCornerRadiuss);
        }
    }

    /**
     * init Image
     */
    protected void initImage() {
        image = new Image(context);
        updateImageRect();
        updateImagePadding();
        button.addComponent(image);
        lastStateIsNormal = false;
        toNormal();
    }

    /**
     * init Text
     * @param parent
     */
    protected void initText(ComponentContainer parent) {
        text = new Text(context);
        updateTextRect();
        updateTextPadding();
        if (typeface != null) text.setFont(typeface);
        text.setMaxTextLines(maxLines);
        text.setTextSize(textSize, Text.TextSizeType.VP);
        text.setTextAlignment(textGravity);
        text.setTruncationMode(ellipsize);
        if (ellipsize == TruncationMode.AUTO_SCROLLING) {
            text.setMultipleLine(false);
            text.setAutoScrollingCount(Text.AUTO_SCROLLING_FOREVER);
            text.setScrollable(true);
            text.setFocusable(1);
            text.setTouchFocusable(true);
            text.startAutoScrolling();
        }
        parent.addComponent(text);
    }

    /**
     * init SubText
     * @param parent
     */
    protected void initSubText(ComponentContainer parent) {
        if (!containsSubText) return;
        subText = new Text(context);
        updateSubTextRect();
        updateSubTextPadding();
        if (subTypeface != null) subText.setFont(subTypeface);
        subText.setMaxTextLines(maxLines);
        subText.setTextSize(subTextSize,Text.TextSizeType.VP);
        subText.setTextAlignment(subTextGravity);
        subText.setTruncationMode(subEllipsize);
        if (subEllipsize == TruncationMode.AUTO_SCROLLING) {
            subText.setMultipleLine(false);
            subText.setAutoScrollingCount(Text.AUTO_SCROLLING_FOREVER);
            subText.setScrollable(true);
            subText.setFocusable(1);
            subText.setTouchFocusable(true);
            subText.startAutoScrolling();
        }
        parent.addComponent(subText);
    }

    /**
     * init CircleButton Drawable
     */
    protected void initCircleButtonDrawable() {
        if (rippleEffectWorks) {
//            ShapeElement gradientDrawable = isRound ?
//                    Util.getOvalDrawable(button, unable ? unableColor() : normalColor()) :
//                    Util.getRectangleDrawable(button, buttonCornerRadius, unable ? unableColor() : normalColor());
//            RippleDrawable rippleDrawable = new RippleDrawable(
//                    ColorStateList.valueOf(highlightedColor()),
//                    gradientDrawable,
//                    null);
//            Util.setDrawable(button, rippleDrawable);
//            this.rippleDrawable = rippleDrawable;
        } else {
            if (isRound)
                nonRippleBitmapDrawable = Util.getOvalStateListBitmapDrawable(
                        button,
                        buttonRadius,
                        normalColor(),
                        highlightedColor(),
                        unableColor());
            else
                nonRippleBitmapDrawable = Util.getRectangleStateListBitmapDrawable(
                        button,
                        buttonWidth,
                        buttonHeight,
                        buttonCornerRadius,
                        normalColor(),
                        highlightedColor(),
                        unableColor());
            if (isNeededColorAnimation()) {
                // Then we need to create 2 drawables to perform the color-transaction effect.
                // Because gradient-drawable is able to change the color,
                // but not able to perform a click-effect.
                nonRippleGradientDrawable = Util.getOvalDrawable(button, unable ? unableColor() : normalColor());
            }
            Util.setDrawable(button, nonRippleBitmapDrawable);
        }
    }

    /**
     * init CircleButton
     */
    protected void initCircleButton() {
        button = (StackLayout) findComponentById(ResourceTable.Id_button);
        StackLayout.LayoutConfig params = (LayoutConfig) button.getLayoutConfig();
        params.width = buttonRadius * 2;
        params.height = buttonRadius * 2;
        button.setLayoutConfig(params);
        button.setEnabled(!unable);
        button.setClickedListener(new ClickedListener() {
            @Override
            public void onClick(Component c) {
                if (!touchable) return;
                if (listener != null) listener.onButtonClick(index, BoomButton.this);
                if (onBMClickListener != null) onBMClickListener.onBoomButtonClick(index);
            }
        });

        initCircleButtonDrawable();

        button.setTouchEventListener(new TouchEventListener() {
            @Override
            public boolean onTouchEvent(Component c, TouchEvent event) {
                if (!touchable) return false;
                final int action = event.getAction();
                final boolean pointerUp = action == TouchEvent.OTHER_POINT_UP;
                final int skipIndex = pointerUp ? event.getIndex() : -1;

                float sumX = 0, sumY = 0;
                final int count = event.getPointerCount();
                // 把所有还在触摸的手指的位置x，y加起来，后面求平均数，算出中心焦点
                for (int i = 0; i < count; i++) {
                    if (skipIndex == i) {
                        // 跳过非主要指针的抬起动作
                        continue;
                    }
                    sumX += event.getPointerPosition(i).getX();
                    sumY += event.getPointerPosition(i).getY();
                }
                final int div = pointerUp ? count - 1 : count;
                // 求平均值，算出中心焦点
                final float focusX = sumX / div;
                final float focusY = sumY / div;

                switch (action) {
                    case TouchEvent.PRIMARY_POINT_DOWN:
                        if (Util.pointInView(new Point(focusX, focusY), button)) {
                            toHighlighted();
                            ableToHighlight = true;
                        }
                        break;
                    case TouchEvent.POINT_MOVE:
                        if (Util.pointInView(new Point(focusX, focusY), button)) {
                            toHighlighted();
                        } else {
                            ableToHighlight = false;
                            toNormal();
                        }
                        break;
                    case TouchEvent.OTHER_POINT_UP:
                    case TouchEvent.CANCEL:
                        ableToHighlight = false;
                        toNormal();
                        break;
                }
                return false;
            }
        });
    }

    /**
     * init HamButton Drawable
     */
    protected void initHamButtonDrawable() {
        if (rippleEffectWorks) {
//            RippleDrawable rippleDrawable = new RippleDrawable(
//                    ColorStateList.valueOf(highlightedColor()),
//                    Util.getRectangleDrawable(button, buttonCornerRadius, unable ? unableColor() : normalColor()),
//                    null);
//            Util.setDrawable(button, rippleDrawable);
//            this.rippleDrawable = rippleDrawable;
        } else {
            nonRippleBitmapDrawable = Util.getRectangleStateListBitmapDrawable(
                    button,
                    buttonWidth,
                    buttonHeight,
                    buttonCornerRadius,
                    normalColor(),
                    highlightedColor(),
                    unableColor());
            if (isNeededColorAnimation()) {
                // Then we need to create 2 drawables to perform the color-transaction effect.
                // Because gradient-drawable is able to change the color,
                // but not able to perform a click-effect.
                nonRippleGradientDrawable = Util.getRectangleDrawable(button, buttonCornerRadius, unable ? unableColor() : normalColor());
            }
            Util.setDrawable(button, nonRippleBitmapDrawable);
        }
    }

    /**
     * init HamButton
     */
    protected void initHamButton() {
        button = (StackLayout) findComponentById(ResourceTable.Id_button);
        LayoutConfig params = (LayoutConfig) button.getLayoutConfig();
        params.width = buttonWidth;
        params.height = buttonHeight;
        button.setLayoutConfig(params);
        button.setEnabled(!unable);
        button.setClickedListener(new ClickedListener() {
            @Override
            public void onClick(Component v) {
                if (!touchable) return;
                if (listener != null) listener.onButtonClick(index, BoomButton.this);
                if (onBMClickListener != null) onBMClickListener.onBoomButtonClick(index);
            }
        });

        initHamButtonDrawable();

        button.setTouchEventListener(new TouchEventListener() {
            @Override
            public boolean onTouchEvent(Component v, TouchEvent event) {
                if (!touchable) return false;

                final int action = event.getAction();
                final boolean pointerUp = action == TouchEvent.OTHER_POINT_UP;
                final int skipIndex = pointerUp ? event.getIndex() : -1;

                float sumX = 0, sumY = 0;
                final int count = event.getPointerCount();
                // 把所有还在触摸的手指的位置x，y加起来，后面求平均数，算出中心焦点
                for (int i = 0; i < count; i++) {
                    if (skipIndex == i) {
                        // 跳过非主要指针的抬起动作
                        continue;
                    }
                    sumX += event.getPointerPosition(i).getX();
                    sumY += event.getPointerPosition(i).getY();
                }
                final int div = pointerUp ? count - 1 : count;
                // 求平均值，算出中心焦点
                final float focusX = sumX / div;
                final float focusY = sumY / div;

                switch (action) {
                    case TouchEvent.PRIMARY_POINT_DOWN:
                        if (Util.pointInView(new Point(focusX, focusY), button)) {
                            toHighlighted();
                            ableToHighlight = true;
                        }
                        break;
                    case TouchEvent.POINT_MOVE:
                        if (Util.pointInView(new Point(focusX, focusY), button)) {
                            toHighlighted();
                        } else {
                            ableToHighlight = false;
                            toNormal();
                        }
                        break;
                    case TouchEvent.OTHER_POINT_UP:
                    case TouchEvent.CANCEL:
                        ableToHighlight = false;
                        toNormal();
                        break;
                }
                return false;
            }
        });
    }

    /**
     * place
     * @param left
     * @param top
     * @param width
     * @param height
     * @return
     */
    public StackLayout.LayoutConfig place(int left, int top, int width, int height) {
        StackLayout.LayoutConfig layoutParams = new StackLayout.LayoutConfig(width, height);
        layoutParams.setMarginLeft(left);
        layoutParams.setMarginTop(top);
        setLayoutConfig(layoutParams);
        return layoutParams;
    }

    /**
     * When the parameters about image are changed by builder, the corresponding builder should
     * call this method to update the image on the Image.
     */
    void updateImage() {
        if (lastStateIsNormal) toNormalImage();
        else toHighlightedImage();
    }

    /**
     * update Text
     */
    void updateText() {
        if (lastStateIsNormal) toNormalText();
        else toHighlightedText();
    }

    /**
     * update SubText
     */
    void updateSubText() {
        if (lastStateIsNormal) toNormalSubText();
        else toHighlightedSubText();
    }

    /**
     * update ImageRect
     */
    void updateImageRect() {
        StackLayout.LayoutConfig params = new LayoutConfig(
                imageRect.right - imageRect.left,
                imageRect.bottom - imageRect.top);
        //params.leftMargin = imageRect.left;
        params.setMarginLeft(imageRect.left);
        //params.topMargin = imageRect.top;
        params.setMarginTop(imageRect.top);
        if (image != null) image.setLayoutConfig(params);
    }

    /**
     * update ImagePadding
     */
    void updateImagePadding() {
        if (imagePadding != null && image != null) {
            image.setPadding(
                    imagePadding.left,
                    imagePadding.top,
                    imagePadding.right,
                    imagePadding.bottom);
        }
    }

    /**
     * update TextRect
     */
    void updateTextRect() {
        StackLayout.LayoutConfig params = new LayoutConfig(
                textRect.right - textRect.left,
                textRect.bottom - textRect.top);
        params.setMarginLeft(textRect.left);
        params.setMarginTop(textRect.top);
        if (text != null) text.setLayoutConfig(params);
    }

    /**
     * update TextPadding
     */
    void updateTextPadding() {
        if (textPadding != null && text != null) {
            text.setPadding(
                    textPadding.left,
                    textPadding.top,
                    textPadding.right,
                    textPadding.bottom);
        }
    }

    /**
     * update SubTextRect
     */
    void updateSubTextRect() {
        StackLayout.LayoutConfig params = new LayoutConfig(
                subTextRect.right - subTextRect.left,
                subTextRect.bottom - subTextRect.top);
        params.setMarginLeft(subTextRect.left);
        params.setMarginTop(subTextRect.top);
        if (subText != null) subText.setLayoutConfig(params);
    }

    /**
     * update SubTextPadding
     */
    void updateSubTextPadding() {
        if (subTextPadding != null && subText != null) {
            subText.setPadding(
                    subTextPadding.left,
                    subTextPadding.top,
                    subTextPadding.right,
                    subTextPadding.bottom);
        }
    }

    /**
     * update Button Drawable
     */
    void updateButtonDrawable() {
        if (buttonEnum == ButtonEnum.SimpleCircle
                || buttonEnum == ButtonEnum.TextInsideCircle
                || buttonEnum == ButtonEnum.TextOutsideCircle) {
            initCircleButtonDrawable();
        } else initHamButtonDrawable();
    }

    /**
     * update Unable
     */
    void updateUnable() {
        if (rippleEffectWorks) updateButtonDrawable();
        button.setEnabled(!unable);
        if (lastStateIsNormal) {
            toNormalImage();
            toNormalText();
            toNormalSubText();
        } else {
            toHighlightedImage();
            toHighlightedText();
            toHighlightedSubText();
        }
    }

    /**
     * to Normal Image
     */
    protected void toNormalImage() {
        if (unable) Util.setDrawable(image, unableImageRes, unableImageDrawable);
        else Util.setDrawable(image, normalImageRes, normalImageDrawable);
    }

    /**
     * to Highlighted Image
     */
    protected void toHighlightedImage() {
        if (unable) Util.setDrawable(image, unableImageRes, unableImageDrawable);
        else Util.setDrawable(image, highlightedImageRes, highlightedImageDrawable);
    }

    /**
     * to Normal Text
     */
    protected void toNormalText() {
        if (unable) {
            Util.setText(text, unableTextRes, unableText);
            Util.setTextColor(text, unableTextColorRes, unableTextColor);
        } else {
            Util.setText(text, normalTextRes, normalText);
            Util.setTextColor(text, normalTextColorRes, normalTextColor);
        }
    }

    /**
     * to Highlighted Text
     */
    protected void toHighlightedText() {
        if (unable) {
            Util.setText(text, unableTextRes, unableText);
            Util.setTextColor(text, unableTextColorRes, unableTextColor);
        } else {
            Util.setText(text, highlightedTextRes, highlightedText);
            Util.setTextColor(text, highlightedTextColorRes, highlightedTextColor);
        }
    }

    /**
     * to Normal SubText
     */
    protected void toNormalSubText()  {

        if (unable) {
            Util.setText(subText, subUnableTextRes, subUnableText);
            Util.setTextColor(subText, subUnableTextColorRes, subUnableTextColor);
        } else {
            Util.setText(subText, subNormalTextRes, subNormalText);
            Util.setTextColor(subText, subNormalTextColorRes, subNormalTextColor);
        }

    }

    /**
     * to Highlighted SubText
     */
    protected void toHighlightedSubText() {
        if (unable) {
            Util.setText(subText, subUnableTextRes, subUnableText);
            Util.setTextColor(subText, subUnableTextColorRes, subUnableTextColor);
        } else {
            Util.setText(subText, subHighlightedTextRes, subHighlightedText);
            Util.setTextColor(subText, subHighlightedTextColorRes, subHighlightedTextColor);
        }
    }

    /**
     * pieceColor
     * @return
     */
    public Color pieceColor() {
        if (pieceColor == null && pieceColorRes == 0)
            if (unable) return unableColor();
            else return normalColor();
        else if (pieceColor == null) return Util.getColor(context, pieceColorRes);
        else return Util.getColor(context, pieceColorRes, pieceColor);
    }

    /**
     * button Color
     * @return
     */
    public Color buttonColor() {
        if (unable) return unableColor();
        else return normalColor();
    }

    /**
     * is Needed Color Animation
     * @return
     */
    public boolean isNeededColorAnimation() {
        Color pieceColors = pieceColor();
        if (unable) return pieceColors.equals(unableColor()) != true;
        else return pieceColors.equals(normalColor()) != true;
    }

    /**
     * set Enabled
     * @param enabled
     */
    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        unable = !enabled;
    }

    /**
     * willShow
     */
    public void willShow() {
        touchable = false;
        if (!rippleEffectWorks && isNeededColorAnimation())
            Util.setDrawable(button, nonRippleGradientDrawable);
        else updateButtonDrawable();
    }

    /**
     * didShow
     */
    public void didShow() {
        touchable = true;
        if (!rippleEffectWorks && isNeededColorAnimation())
            Util.setDrawable(button, nonRippleBitmapDrawable);
        if (text != null) text.setSelected(true);
        if (subText != null) subText.setSelected(true);
    }

    /**
     * willHide
     */
    public void willHide() {
        touchable = false;
        if (!rippleEffectWorks && isNeededColorAnimation())
            Util.setDrawable(button, nonRippleGradientDrawable);
    }

    /**
     * didHide
     */
    public void didHide() {
        if (text != null) text.setSelected(false);
        if (subText != null) subText.setSelected(false);
    }

    /**
     * hide All GoneViews
     */
    public void hideAllGoneViews() {
        for (Component view : goneViews()) view.setAlpha(0);
    }

    /**
     * prepare Color Transform Animation
     * @return
     */
    public boolean prepareColorTransformAnimation() {
        if (rippleEffectWorks) {
            if (rippleDrawable == null) {
                throw new RuntimeException("Background drawable is null!");
            }
        } else if (nonRippleGradientDrawable == null) {
            throw new RuntimeException("Background drawable is null!");
        }
        return rippleEffectWorks;
    }

    /**
     * set NonRippleButtonColor
     * @param color
     */
    public void setNonRippleButtonColor(Color color) {
        nonRippleGradientDrawable.setRgbColor(RgbColor.fromArgbInt(color.getValue()));
    }

    /**
     * set RippleButtonColor
     * @param color
     */
    public void setRippleButtonColor(Color color) {
        ((ShapeElement)rippleDrawable.getCurrentElement()).setRgbColor(RgbColor.fromArgbInt(color.getValue()));
    }

    /**
     * clear Animation
     */
    public void clearAnimation() {
        try {
            if (ap != null && ap.isRunning()) {
                ap.reset();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * create Animator Property
     * @return
     */
    @Override
    public AnimatorProperty createAnimatorProperty() {
        ap = super.createAnimatorProperty();
        return ap;
    }

    /**
     * set Clickable
     * @param clickable
     */
    @Override
    public void setClickable(boolean clickable) {
        super.setClickable(clickable);
        button.setClickable(clickable);
    }

    /**
     * normal Color
     * @return
     */
    protected Color normalColor() {
        return Util.getColor(context, normalColorRes, normalColor);
    }

    /**
     * high lighted Color
     * @return
     */
    protected Color highlightedColor(){
        return Util.getColor(context, highlightedColorRes, highlightedColor);
    }

    /**
     * unableColor
     * @return
     */
    protected Color unableColor() {
        return Util.getColor(context, unableColorRes, unableColor);
    }

    /**
     * Get the layout view of a boom button.
     *
     * @return layout view
     */
    public ComponentContainer getLayout() { return layout; }

    /**
     * Get the button layout view of a boom button.
     *
     * @return button layout view
     */
    public StackLayout getButton() { return button; }

    /**
     * Get the shadow view of a boom button.
     *
     * @return shadow view
     */
    public BMBShadow getShadow() { return shadow; }

    /**
     * Get the image view of a boom button.
     *
     * @return image view
     */
    public Image getImage() {
        return image;
    }

    /**
     * Get the text view of a boom button.
     *
     * @return text view
     */
    public Text getText() {
        return text;
    }

    /**
     * Get the sub text view of a boom button.
     *
     * @return sub text view
     */
    public Text getSubText() {
        return subText;
    }

    public abstract ButtonEnum type();
    public abstract ArrayList<Component> goneViews();
    public abstract ArrayList<Component> rotateViews();
    public abstract int trueWidth();
    public abstract int trueHeight();
    public abstract int contentWidth();
    public abstract int contentHeight();
    protected abstract void toHighlighted();
    protected abstract void toNormal();
    public abstract void setRotateAnchorPoints();
    public abstract void setSelfScaleAnchorPoints();
    public Point centerPoint;
}
